#ifndef VIDEODECODE_H
#define VIDEODECODE_H

//视频解码类
#include <QString>
#include <QImage>
#include <thread>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
}

/*
struct AVFormatContext;
struct AVCodecContext;
struct AVRational;
struct AVPacket;
struct AVFrame;
struct AVCodec;
struct SwsContext;
struct AVBufferRef;
*/

//流类型
enum StreamType
{
    StreamType_Video        = 0,
    StreamType_Audio        = 1,
    StreamType_Text         = 2,
};

//格式类型
enum FormatType
{
    FormatType_RGB24        =   0,
    FormatType_RGB32        =   1,
    FormatType_YUV420P      =   2,
    FormatType_NV12         =   3,
};

//文件状态
enum  FileStatus
{
    FileStatus_OverFileTail     =   0,  //达到文件尾
    FileStatus_OverFileHead     =   1,  //达到文件头
    FileStatus_TrigeException   =   2,  //发生异常
};

//视频流解码回调函数
typedef void (*StreamDecodeCallback)(int nStreamType, int nFormatType, long long llDecodeTs, long long llPlayTs, int width, int height, unsigned char ** pStreamData, int * linesize, void * pUserData);

/*
 * 音频流解码回调函数
 * nSampleRate:采样率（如48000）
 * nChannel:声道数（如2）
 * eSampleFmt:采样格式
 * nChannelLaytout:声道布局
*/
typedef  void (*AudioStreamDecodeCallback)(int nSampleRate, int nChannel, int nSampleFmt, uint64_t nChannelLaytout, unsigned char *pStreamData, int nStreamDataLength, void * pUserData);

//文件状态回调函数
typedef  void (*FileStatusCallback)(int FileStatus, int nErrorCode, void * pUserData);

class VideoDecode
{
public:
    VideoDecode();
    ~VideoDecode();

public:
    void globalInit();//初始化ffmpeg库（整个程序中只需加载一次）
    void globalUnInit();//反初始化ffmpeg库（整个程序中只需加载一次）

public:
    void setStreamDecodeCallback(StreamDecodeCallback funStreamDecodeCallback, AudioStreamDecodeCallback funAudioStreamDecodeCallback, void * userData);
    void setFileStatusCallback(FileStatusCallback funFileStatusCallback, void * userData);

    void setHWDecoder(bool flag);                 // 是否使用硬件解码器
    bool isHWDecoder();
    bool open(const QString& url);              // 打开媒体文件，或者流媒体rtmp、strp、http
    void close();                               // 关闭
    bool isClose();
    void setPause(bool pause);
    bool isPause();

public:
    void decodeProccessThread();                //解码线程
    static QImage ConvertRGB24FrameToQImage(unsigned char *data, int width, int height);

protected:
    void initHWDecoder(const AVCodec *codec);
    bool dataCopy();    //硬件解码完成需要将数据从GPU复制到CPU
    void freeDecode();
    qreal rationalToDouble(AVRational* rational);

private:
    // FFmpeg 相关对象
    AVFormatContext *formatCtx = nullptr;
    AVCodecContext *codecCtx = nullptr;
    AVFrame *frame = nullptr, *rgbFrame = nullptr;
    AVFrame *frameHW = nullptr;
    SwsContext *swsCtx = nullptr;
    uchar* buffer = nullptr;                      // YUV图像需要转换位RGBA图像，这里保存转换后的图形数据
    AVPacket* packet = nullptr;
    int videoStreamIndex = -1;  // 视频流索引
    qint64 totalTime    = 0;                    // 视频总时长
    qint64 totalFrames  = 0;                    // 视频总帧数
    qint64 obtainFrames = 0;                    // 视频当前获取到的帧数
    qint64 pts          = 0;                    // 图像帧的显示时间
    qreal  frameRate    = 0;                    // 视频帧率
    int  width = 0;         //视频分辨率大小width
    int  height = 0;        //视频分辨率大小height

    AVCodecContext *codecCtx_Audio = nullptr;
    int audioStreamIndex = -1;
    AVFrame *frame_audio = nullptr;
    SwrContext *swrCtxAudo = nullptr;
    uint8_t * bufferAudio = nullptr;
    int bufferAudioSize = 0;

    std::vector<int> vecHWDeviceTypes;            // 保存当前环境支持的硬件解码器
    AVBufferRef* hw_device_ctx = nullptr;         // 对数据缓冲区的引用
    bool   hwDecoderFlag = false;                 // 记录是否使用硬件解码

    std::thread threadDecode;
    bool stopWorkFlag = true;
    bool pauseWorkFlag = false;
    StreamDecodeCallback funCallbackByStreamDecode = nullptr;
    AudioStreamDecodeCallback funCallbackByAudioStreamDecode = nullptr;
    void * userDataByStreamDecode = nullptr;
    FileStatusCallback funCallbackByFileStatus = nullptr;
    void * userDataByFileStatus = nullptr;
};

#endif // VIDEODECODE_H
